package com.xiaoshuidi.cloud.module.contract.mq.consumer;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.tenant.core.util.TenantUtils;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.OrderFeesDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.RentOrderDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.ContractRoomDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.SettlementDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.roommeta.ContractRoommateDO;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.SettlementMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.roommeta.ContractRoommateMapper;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderPayStatusEnum;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractNodeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractStatusEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.CustomerTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.settlement.SettlementStatusEnum;
import com.xiaoshuidi.cloud.module.contract.service.bill.OrderFeesService;
import com.xiaoshuidi.cloud.module.contract.service.bill.RentOrderService;
import com.xiaoshuidi.cloud.module.contract.service.contract.ContractApplyService;
import com.xiaoshuidi.cloud.module.contract.service.contract.ContractRoomService;
import com.xiaoshuidi.cloud.module.contract.service.roommate.ContractRoommateService;
import com.xiaoshuidi.cloud.module.finance.enums.finance.SourceBizTypeEnum;
import com.xiaoshuidi.cloud.module.finance.mq.PayResultConstants;
import com.xiaoshuidi.cloud.module.finance.mq.message.PayResultMessage;
import com.xiaoshuidi.cloud.module.iot.common.ResultModel;
import com.xiaoshuidi.cloud.module.member.api.point.PointsApi;
import com.xiaoshuidi.cloud.module.member.enums.point.PointRecordEnums;
import com.xiaoshuidi.cloud.module.rooms.api.sdiot.IotRoomDeviceApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 支付结果通知
 */
@Slf4j
@Component(PayResultConstants.CONSUMER)
public class PayResultConsumer implements Consumer<PayResultMessage> {
    @Resource
    private RentOrderService rentOrderService;
    @Resource
    private OrderFeesService orderFeesService;
    @Resource
    private ContractRoomService contractRoomService;
    @Resource
    private ContractApplyService contractApplyService;
    @Resource
    private ContractRoommateService contractRoommateService;
    @Resource
    private PointsApi pointsApi;
    @Resource
    private IotRoomDeviceApi iotRoomDeviceApi;
    @Resource
    private ContractRoommateMapper contractRoommateMapper;

    @Resource
    private SettlementMapper settlementMapper;

    @Override
    public void accept(PayResultMessage payResultMessage) {
        log.info("支付结果通知: {}", JsonUtils.toJsonString(payResultMessage));
        if (!payResultMessage.getSuccess()) {
            return;
        }
        if (SourceBizTypeEnum.COMPANY_RENT_ORDER.name().equals(payResultMessage.getSourceBizType())) {
            companyRentOrder(payResultMessage);
        } else if (SourceBizTypeEnum.RENT_ORDER.name().equals(payResultMessage.getSourceBizType())
                || SourceBizTypeEnum.OWNER_ORDER.name().equals(payResultMessage.getSourceBizType())) {
            rentOrder(payResultMessage);
            //清算单
        } else if (SourceBizTypeEnum.SETTLEMENT_BILL.name().equals(payResultMessage.getSourceBizType())) {
            settleMentBill(payResultMessage);
        }
    }

    private void settleMentBill(PayResultMessage payResultMessage) {
        TenantUtils.execute(payResultMessage.getTenantId(), () -> {
            SettlementDO settlementDO = settlementMapper.selectById(payResultMessage.getSourceBizId());
            //生效
            settlementMapper.updateById(SettlementDO.builder().id(settlementDO.getId())
                    .clearedStatus(SettlementStatusEnum.CLEAR.getCode()).build());
            //子项也是已生效
            LambdaQueryWrapper<SettlementDO> eq =
                    new LambdaQueryWrapper<SettlementDO>().eq(SettlementDO::getParentSettlementId,
                            settlementDO.getId());
            List<SettlementDO> settlementDOS = settlementMapper.selectList(eq);
            if (CollectionUtils.isNotEmpty(settlementDOS)) {
                List<SettlementDO> collect = settlementDOS.stream().map(m -> SettlementDO.builder().id(m.getId())
                        .clearedStatus(SettlementStatusEnum.CLEAR.getCode()).build()).collect(Collectors.toList());
                settlementMapper.updateBatch(collect, collect.size());
            }
        });

    }

    private void companyRentOrder(PayResultMessage payResultMessage) {
        TenantUtils.execute(payResultMessage.getTenantId(), () -> {
            List<RentOrderDO> rentOrderList = rentOrderService.getRentListByBatchNo(payResultMessage.getSourceBizId());
            if (rentOrderList == null) {
                log.error("支付结果通知: 账单[{}]不存在", payResultMessage.getSourceBizId());
                return;
            }
            LocalDate now = LocalDate.now();
            for (RentOrderDO rentOrderDO : rentOrderList) {
                BigDecimal paid = rentOrderDO.getPaidRentUtilities() == null ? BigDecimal.ZERO :
                        rentOrderDO.getPaidRentUtilities();
                if (paid.compareTo(rentOrderDO.getTotalAmount()) >= 0) {
                    log.info("企业子账单[{}]已支付", rentOrderDO.getOrderNo());
                    return;
                }
                BigDecimal subtract = rentOrderDO.getTotalAmount().subtract(paid);
                log.info("企业账单[{}]批次支付完成，账单[{}/{}]已付{}, 本批次共支付: {}，此账单支付：{}", payResultMessage.getSourceBizId(), rentOrderDO.getId(),
                        rentOrderDO.getOrderNo(), paid,
                        BigDecimal.valueOf(payResultMessage.getPayAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP),
                        subtract);
                // 默认完成支付
                paid = rentOrderDO.getTotalAmount();
                updateCompanyRentOrderAndFees(payResultMessage, rentOrderDO, paid);
                // 逾期账单支付恢复水电
                if (rentOrderDO.getStartTime().compareTo(now) <= 0){
                    rentOrderService.overdueOrderDeal(rentOrderDO);
                }
                // 针对之前已经存在的合同续租需重新下发，后续新增的合同续租不下发密码
                updateIotPassword(payResultMessage, rentOrderDO, paid);
                // 首期账单处理
                if (1 == rentOrderDO.getNum()){
                    contractRoomService.dealOldContract(rentOrderDO.getBusinessId(),null);
                }
            }
        });
    }


    private void rentOrder(PayResultMessage payResultMessage) {
        TenantUtils.execute(payResultMessage.getTenantId(), () -> {
            RentOrderDO rentOrderDO = rentOrderService.getContractRentOrder(Long.valueOf(payResultMessage.getSourceBizId()));
            if (rentOrderDO == null) {
                log.error("支付结果通知: 账单[{}]不存在", payResultMessage.getSourceBizId());
                return;
            }
            BigDecimal paid = rentOrderDO.getPaidRentUtilities() == null ? BigDecimal.ZERO : rentOrderDO.getPaidRentUtilities();
            if (paid.compareTo(rentOrderDO.getTotalAmount()) >= 0) {
                log.info("账单[{}]已支付", rentOrderDO.getOrderNo());
                return;
            }

            log.info("账单[{}/{}]已付{}, 本次支付: {}", rentOrderDO.getId(), rentOrderDO.getOrderNo(),
                    paid, BigDecimal.valueOf(payResultMessage.getPayAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
            paid = paid.multiply(BigDecimal.valueOf(100)).add(BigDecimal.valueOf(payResultMessage.getPayAmount()));
            paid = paid.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
            boolean paidAll = paid.add(payResultMessage.getCouponDiscount() == null ? BigDecimal.ZERO :
                            BigDecimal.valueOf(payResultMessage.getCouponDiscount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP))
                    .compareTo(rentOrderDO.getTotalAmount()) >= 0;
            // TODO 计算分摊
            updateRentOrderAndFees(payResultMessage, rentOrderDO, paid, paidAll);
            if (paidAll) {
                // 针对之前已经存在的合同续租需重新下发，后续新增的合同续租不下发密码
                updateIotPassword(payResultMessage, rentOrderDO, paid);
                addPoints(payResultMessage, rentOrderDO, paid);
                // 逾期账单支付恢复水电
                if (rentOrderDO.getStartTime().compareTo(LocalDate.now()) <= 0){
                    rentOrderService.overdueOrderDeal(rentOrderDO);
                }
                // 首期账单处理
                if (1 == rentOrderDO.getNum()){
                    contractRoomService.dealOldContract(rentOrderDO.getBusinessId(),null);
                }
            }
            //如果账单类型延期申请,则需要更新申请单信息
            if (OrderTypeEnum.EXTENSION.getCode().equals(rentOrderDO.getOrderType())) {
                contractApplyService.paymentCompleted(rentOrderDO.getId());
            }
        });
    }

    private void addPoints(PayResultMessage payResultMessage, RentOrderDO rentOrderDO, BigDecimal paid) {
        if (rentOrderDO.getMemberId() != null) {
            try {
                CommonResult<Boolean> result = pointsApi.plusTenantPoint(rentOrderDO.getMemberId(), rentOrderDO.getRentAmount()
                                .subtract(rentOrderDO.getDiscountAmount())
                                .setScale(0, RoundingMode.HALF_UP).intValue(),
                        PointRecordEnums.RENT.value(), "租金支付");
                log.info("账单支付租金, 添加积分 {}/{}", rentOrderDO.getRentAmount(), result.isSuccess());
            } catch (Exception e) {
                log.error("账单支付租金, 添加积分异常", e);
            }
        }
    }

    private void updateIotPassword(PayResultMessage payResultMessage, RentOrderDO rentOrderDO, BigDecimal paid) {
        if (rentOrderDO.getNum() == 1) {
            ContractRoomDO contract = contractRoomService.get(rentOrderDO.getBusinessId());
            if (contract != null && ContractNodeEnum.RENEWAL.getCode().equals(contract.getContractNode())) {
                //新需求，密码长期有效，针对之前已经存在的合同续租需重新下发，后续新增的合同续租不下发密码
                if(contract.getFromId()!=null){
                    ContractRoomDO contract1 = contractRoomService.get(contract.getFromId());
                    if(contract1.getCreateTime().toLocalDate().isAfter(LocalDate.of(2023,9,18))){
                        //此段目前写死，后续此处整个下发密码整个逻辑可以删掉
                        log.info("新签合同续租不需要下发密码 合同id:{}",contract1.getId());
                        return;
                    }
                }

                try {
                    log.info("清除门锁密码 {} {}", rentOrderDO.getOrderNo(), rentOrderDO.getRoomId());
                    //删除下发密码
                    List<String> mobiles = contractRoommateMapper.selectByContractIdWithOutStatus(rentOrderDO.getBusinessId());
                    ResultModel rs = iotRoomDeviceApi.clearPassword1(rentOrderDO.getRoomId(),mobiles);
                    log.info("删除房间密码:{} resp:{}", rentOrderDO.getRoomId(), JSONObject.toJSONString(rs));
                    if ("0".equals(rs.getCode())) {
                        if(CustomerTypeEnum.PERSONAL.getCode().equals(contract.getCustomerType())){
                            //租客合同续租只下发承住人密码
                            contractRoommateService.issuePassword(rentOrderDO.getRoomId(), contract.getCustomerName(),
                                    contract.getCustomerPhone(), LocalDate.now(), contract.getEndTime(), contract.getSignerName());
                        }else {
                            //原合同结束，则取新合同同住人
                            ContractRoomDO contractRoomDO = contractRoomService.get(contract.getFromId());
                            if(ContractStatusEnum.NORMAL_END.getCode().equals(contractRoomDO.getContractStatus())){
                                List<ContractRoommateDO> roommateDOS = contractRoommateMapper.selectCheckInByContractId(rentOrderDO.getBusinessId());
                                if(CollectionUtils.isNotEmpty(roommateDOS)){
                                    for (ContractRoommateDO roommateDO : roommateDOS) {
                                        contractRoommateService.issuePassword(roommateDO.getRoomId(), roommateDO.getRoommateName(),
                                                roommateDO.getRoommatePhone(), LocalDate.now(), contract.getEndTime(), contract.getSignerName());
                                    }
                                }
                            }else {
                                //原合同未结束，取原合同同住人
                                List<ContractRoommateDO> roommateDOS = contractRoommateMapper.selectCheckInByContractId(contract.getFromId());
                                if(CollectionUtils.isNotEmpty(roommateDOS)){
                                    for (ContractRoommateDO roommateDO : roommateDOS) {
                                        contractRoommateService.issuePassword(roommateDO.getRoomId(), roommateDO.getRoommateName(),
                                                roommateDO.getRoommatePhone(), LocalDate.now(), contract.getEndTime(), contract.getSignerName());
                                    }
                                }
                            }
                        }

                    }
                } catch (Exception e) {
                    log.error("支付完成下发密码异常", e);
                }
            }
        }
    }

    // TODO 扣费
    private void updateRentOrderAndFees(PayResultMessage payResultMessage, RentOrderDO rentOrderDO, BigDecimal paid, boolean paidAll) {
        RentOrderDO update = new RentOrderDO();
        update.setId(rentOrderDO.getId());
                update.setActualPayTime(DateUtil.toLocalDateTime(payResultMessage.getPayTime()));
                update.setPaidRentUtilities(paid);
                update.setActualPaymentAmount(BigDecimal.valueOf(payResultMessage.getPayAmount()).divide(BigDecimal.valueOf(100)));
        update.setCouponDiscount(payResultMessage.getCouponDiscount() != null
                        ? BigDecimal.valueOf(payResultMessage.getCouponDiscount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)
                        : BigDecimal.ZERO);
        update.setPayStatus(paid.add(update.getCouponDiscount()).compareTo(rentOrderDO.getTotalAmount()) >= 0 ?
                OrderPayStatusEnum.PAID.getCode() : OrderPayStatusEnum.PART.getCode());
                update.setPayer(payResultMessage.getTrader());
                update.setPayMethod(payResultMessage.getPayMethod());
                update.setTradeSerialNo(payResultMessage.getFlowNo());

        rentOrderService.updateById(update);

        // 重新计算
        if(paidAll) { // 一次性支付走原方法
            log.info("一次性全额支付");
//            orderFeesService.updateOrderFeePaid(update.getId(), update.getPayStatus());
            orderFeesService.updateOrderFeePaidAmount(update.getId(), update.getPayStatus(), paid, DateUtil.toLocalDateTime(payResultMessage.getPayTime()));
            // 添加账单支付记录,分账查询使用
        } else {
            //  支付金额 paid
            BigDecimal unPaidAmount = BigDecimal.valueOf(update.getActualPaymentAmount().doubleValue()); // 20
            // 查询未支付的订单明细
            List<OrderFeesDO> orderFeeListByOrderId = orderFeesService.getOrderFeeListByOrderId(update.getId());
            for (OrderFeesDO orderFeesDO : orderFeeListByOrderId) {
                BigDecimal amount = orderFeesDO.getAmount();// 账单金额 20
                BigDecimal paidAmount = orderFeesDO.getPaidAmount();// 实际支付 10
                BigDecimal residueAmount = amount.subtract(paidAmount); // 10
                if (unPaidAmount.compareTo(residueAmount) >= 0){
                    log.info("子账单全额付》》》账单金额：{}, 已支付金额：{}, 支付剩余分配: {}", amount, paidAmount, unPaidAmount);
                    // 子项全额够
                    orderFeesService.updateOrderFeeById(orderFeesDO.getId());
                    unPaidAmount = unPaidAmount.subtract(residueAmount);
                    // 添加账单支付记录,分账查询使用
                } else {
                    log.info("子账单部分付》》》账单金额：{}, 已支付金额：{}, 支付剩余分配: {}", amount, paidAmount, unPaidAmount);
                    // 子项不够，处理完当前跳出
                    orderFeesService.updateOrderFeeAmountById(orderFeesDO.getId(), unPaidAmount);
                    // 添加账单支付记录,分账查询使用
                    break;
                }
            }
        }
    }

    private void updateCompanyRentOrderAndFees(PayResultMessage payResultMessage, RentOrderDO rentOrderDO, BigDecimal paid) {
        RentOrderDO update = new RentOrderDO();
        update.setId(rentOrderDO.getId());
        update.setActualPayTime(DateUtil.toLocalDateTime(payResultMessage.getPayTime()));
        update.setPaidRentUtilities(paid);
        update.setActualPaymentAmount((rentOrderDO.getTotalAmount()!=null?rentOrderDO.getTotalAmount():BigDecimal.ZERO)
                .subtract(rentOrderDO.getPaidRentUtilities()!=null? rentOrderDO.getPaidRentUtilities():BigDecimal.ZERO));
        update.setCouponDiscount(payResultMessage.getCouponDiscount() != null
                ? BigDecimal.valueOf(payResultMessage.getCouponDiscount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)
                : BigDecimal.ZERO);
        update.setPayStatus(paid.add(update.getCouponDiscount()).compareTo(rentOrderDO.getTotalAmount()) >= 0 ?
                OrderPayStatusEnum.PAID.getCode() : OrderPayStatusEnum.PART.getCode());
        update.setPayer(payResultMessage.getTrader());
        update.setPayMethod(payResultMessage.getPayMethod());
        update.setTradeSerialNo(payResultMessage.getFlowNo());
        rentOrderService.updateById(update);
//        orderFeesService.updateOrderFeePaid(update.getId(), update.getPayStatus());
        orderFeesService.updateOrderFeePaidAmount(update.getId(), update.getPayStatus(), paid, DateUtil.toLocalDateTime(payResultMessage.getPayTime()));
    }
}
